package com.basker.pisces.domain.entity.data;

import java.util.List;
import java.util.Set;
import java.util.function.Consumer;

import com.basker.pisces.domain.property.InvalidPropertyValueException;
import com.basker.pisces.service.exception.IObjectErrorsWrapper.ObjectError;
import com.google.common.collect.Sets;

/**
 * 执行
 * {@link IDataEntity#input(Object, InputOption)},
 * {@link IDataEntity#batchInput(List, InputOption)},
 * {@link IDataEntity#inputChildren(String, List, InputOption)}的选项参数
 * <p>
 * <br/>
 * <li>1.支持新增导入和更新导入,通过mode字段控制
 * <li>2.如果发生非法的录入值，默认会抛出异常，可以控制异常是否抛出，也可以自己处理异常
 * <li>3.可以指定导入子表前是否清空子表
 *
 * @author hangwen
 */
public class InputOption {

    /**
     * 新增导入模式
     */
    public static final int INPUT_MODE_INSERT = 1;

    /**
     * 更新导入模式
     */
    public static final int INPUT_MODE_UPDATE = 2;

    private static final Consumer<InvalidPropertyValueException> THROW_ERROR_HANDLER = ex -> {
        if (ex.hasErrors()) {
            throw ex;
        }
    };

    private static final Consumer<InvalidPropertyValueException> SILENT_ERROR_HANDLER = ex -> {

    };

    private int mode = INPUT_MODE_INSERT | INPUT_MODE_UPDATE;

    private Set<String> clearChildren;

    private boolean enableThrowInvalidPropertyValueException = true;

    private InvalidPropertyValueException invalidPropertyValueException;

    private Consumer<InvalidPropertyValueException> errorHandler;

    public InputOption() {

    }

    public InputOption(boolean enableThrowInvalidPropertyValueException) {
        this(INPUT_MODE_INSERT | INPUT_MODE_UPDATE, enableThrowInvalidPropertyValueException);
    }

    public InputOption(int inputMode, boolean enableThrowInvalidPropertyValueException) {
        this.mode = inputMode;
        this.enableThrowInvalidPropertyValueException = enableThrowInvalidPropertyValueException;
    }

    public InvalidPropertyValueException getCause() {
        if (this.invalidPropertyValueException != null && this.invalidPropertyValueException.hasErrors()) {
            return this.invalidPropertyValueException;
        }

        return null;
    }

    /**
     * 获取导入模式，默认两者都支持
     *
     * @return
     * @see #INPUT_MODE_INSERT
     * @see #INPUT_MODE_UPDATE
     */
    public int getMode() {
        return mode;
    }

    public void setMode(int mode) {
        this.mode = mode;
    }

    public boolean isClearChildren(String childEntityName) {
        return clearChildren != null && this.clearChildren.contains(childEntityName);
    }

    public void setClearChildren(String childEntityName) {
        if (this.clearChildren == null) {
            this.clearChildren = Sets.newHashSet();
        }

        this.clearChildren.add(childEntityName);
    }

    /**
     * 当导入过程有错误时，设置是否需要显示抛出异常
     *
     * @param enableThrowInvalidPropertyValueException
     */
    public void setEnableThrowInvalidPropertyValueException(boolean enableThrowInvalidPropertyValueException) {
        this.enableThrowInvalidPropertyValueException = enableThrowInvalidPropertyValueException;
    }

    /**
     * 如果有错误则处理，默认的处理器会抛出异常
     */
    void handleInvalidPropertyValueException() {
        if (this.invalidPropertyValueException != null) {
            this.getErrorHandler().accept(this.invalidPropertyValueException);
        }
    }

    /**
     * 合并错误
     *
     * @param invalidPropertyValueException
     * @param inputObject
     */
    void mergeInvalidPropertyValueException(InvalidPropertyValueException invalidPropertyValueException,
                                            Object inputObject) {
        List<ObjectError> errors = invalidPropertyValueException.getErrors();

        for (ObjectError objectError : errors) {
            objectError.setObject(inputObject);
        }

        if (this.invalidPropertyValueException == null) {
            this.invalidPropertyValueException = new InvalidPropertyValueException();
        }

        this.invalidPropertyValueException.merge(invalidPropertyValueException);
    }

    /**
     * 是否支持新增导入
     *
     * @return
     */
    boolean supportInsertMode() {
        return (this.mode & INPUT_MODE_INSERT) == INPUT_MODE_INSERT;
    }

    /**
     * 是否支持更新导入
     *
     * @return
     */
    boolean supportUpdateMode() {
        return (this.mode & INPUT_MODE_UPDATE) == INPUT_MODE_UPDATE;
    }

    private Consumer<InvalidPropertyValueException> getErrorHandler() {
        if (this.errorHandler != null) {
            return errorHandler;
        }

        return this.enableThrowInvalidPropertyValueException ? THROW_ERROR_HANDLER : SILENT_ERROR_HANDLER;
    }

    /**
     * 发生错误时调用该方法,用于处理错误对象,如果设置了自定义的errorHandler，则{@link #enableThrowInvalidPropertyValueException}不再起作用
     *
     * @param errorHandler
     */
    public void setErrorHandler(Consumer<InvalidPropertyValueException> errorHandler) {
        this.errorHandler = errorHandler;
    }
}
